import datetime

from cc.appcoin2.support.feed.py_market_data_client import PyMarketDataClient
from coin.exchange.base.kr_rest.py_product import PyProduct
from coin.base.datetime_util import to_timestamp_int
from coin.proto.coin_request_pb2 import KlineRequestProto
from coin.proto.coin_market_query_pb2 import (
    ProductKline,
    ProductKlineElement)
import coin.feed.fastfeed.feed_pb2 as ff_pb2


_kline_period_map = {
    '1m': 'KLINE_INTERVAL_1MIN',
    '5m': 'KLINE_INTERVAL_5MIN',
    '15m': 'KLINE_INTERVAL_15MIN',
    '1h': 'KLINE_INTERVAL_1HOUR',
    '4h': 'KLINE_INTERVAL_4HOUR',
    '1d': 'KLINE_INTERVAL_1DAY',
    '1W': 'KLINE_INTERVAL_1WEEK',
    '1M': 'KLINE_INTERVAL_1MON',
}


def _convert_kline(kline_req, kline_snapshot):
  assert isinstance(kline_snapshot, ff_pb2.KlineSnapshot)
  klines = []
  for kline in kline_snapshot.kline:
    # use HasField after protobuf upgraded to version newer than 3.15
    buy_volume = kline.buy_volume if kline.buy_volume > 0 else None
    sell_volume = None
    if buy_volume is not None:
      assert kline.volume >= kline.buy_volume, kline
      sell_volume = kline.volume - kline.buy_volume
    turnover = kline.turnover if kline.turnover else None
    buy_turnover = kline.buy_turnover if kline.buy_turnover else None
    sell_turnover = None
    if turnover is not None and buy_turnover is not None:
      assert kline.turnover >= kline.buy_turnover, kline
      sell_turnover = kline.turnover - kline.buy_turnover
    klines.append(ProductKlineElement(
        kline_timestamp=kline.open_timestamp,
        open=kline.open,
        high=kline.high,
        low=kline.low,
        close=kline.close,
        volume=kline.volume,
        buy_volume=buy_volume,
        sell_volume=sell_volume,
        turnover=turnover,
        buy_turnover=buy_turnover,
        sell_turnover=sell_turnover,))
  klines = [kline for kline in klines if
            kline.kline_timestamp >= kline_req.start_timestamp and
            kline.kline_timestamp < kline_req.end_timestamp]
  return ProductKline(
        market_type=kline_req.market_type,
        exchange=kline_req.exchange,
        symbol=kline_req.symbol,
        klines=klines)


class PyPublicClient(object):
  def __init__(self):
    self._client = PyMarketDataClient()

  def query_history_kline(self, kline_request):
    assert isinstance(kline_request, KlineRequestProto)
    assert kline_request.HasField('start_timestamp')
    assert kline_request.HasField('end_timestamp')
    assert kline_request.end_timestamp >= kline_request.start_timestamp
    kline_request.kline_period = _kline_period_map[kline_request.kline_period]
    raw_data = self._client.query_klines(kline_request.SerializeToString())
    data =  ff_pb2.Feed()
    data.ParseFromString(raw_data)
    converted = _convert_kline(kline_request, data.kline_snapshot)
    for kline in converted.klines:
      assert kline.kline_timestamp >= kline_request.start_timestamp
      assert kline.kline_timestamp < kline_request.end_timestamp
    return converted


if __name__ == "__main__":
  start_time = datetime.datetime(2022, 10, 31)
  end_time = datetime.datetime(2022, 10, 31, 2)
  product = PyProduct.from_norm(
      'Futures:Binance:BTC-USDT.PERPETUAL', to_timestamp_int(start_time))
  client = PyPublicClient()
  kline_request = KlineRequestProto(
      market_type=product.market_name,
      exchange=product.exchange_name,
      symbol=product.symbol,
      api_version='v1',
      start_timestamp=to_timestamp_int(start_time),
      end_timestamp=to_timestamp_int(end_time),
      kline_period='1h'
  )
  print(client.query_history_kline(kline_request))
